/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    usart.c
  * @brief   This file provides code for the configuration
  *          of the USART instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

UART_HandleTypeDef huart4;
DMA_HandleTypeDef hdma_uart4_rx;
DMA_HandleTypeDef hdma_uart4_tx;

static void uart_dma_idle_irq_callback(UART_HandleTypeDef *huart, uint16_t Size);
static void uart_dma_tx_complete_callback(UART_HandleTypeDef *huart);

/* USER CODE END 0 */

/* UART4 init function */
void MX_UART4_Init(void)
{

  /* USER CODE BEGIN UART4_Init 0 */

  /* USER CODE END UART4_Init 0 */

  /* USER CODE BEGIN UART4_Init 1 */

  /* USER CODE END UART4_Init 1 */
  huart4.Instance = UART4;
  huart4.Init.BaudRate = 115200;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart4) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart4, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart4, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart4) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN UART4_Init 2 */
  
  /* 注册串口DMA空闲中断回调函数 */
  HAL_UART_RegisterRxEventCallback(&huart4, uart_dma_idle_irq_callback);

  /* 注册串口DMA发送完成中断回调函数 */
  
  HAL_UART_RegisterCallback(&huart4, HAL_UART_TX_COMPLETE_CB_ID, \
                            uart_dma_tx_complete_callback);

  /* USER CODE END UART4_Init 2 */

}

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
  if(uartHandle->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspInit 0 */

  /* USER CODE END UART4_MspInit 0 */

  /** Initializes the peripherals clock
  */
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART4;
  PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }    
  /* UART4 clock enable */
  __HAL_RCC_UART4_CLK_ENABLE();    
  __HAL_RCC_GPIOI_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  /**UART4 GPIO Configuration
  PI9     ------> UART4_RX
  PA0_C     ------> UART4_TX
  */
  GPIO_InitStruct.Pin = U4_RX_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
  HAL_GPIO_Init(U4_RX_GPIO_Port, &GPIO_InitStruct);    
  GPIO_InitStruct.Pin = U4_TX_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
  HAL_GPIO_Init(U4_TX_GPIO_Port, &GPIO_InitStruct);    
  /* UART4 DMA Init */
  /* UART4_RX Init */
  hdma_uart4_rx.Instance = DMA1_Stream0;
  hdma_uart4_rx.Init.Request = DMA_REQUEST_UART4_RX;
  hdma_uart4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_uart4_rx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_uart4_rx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_uart4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_uart4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_uart4_rx.Init.Mode = DMA_NORMAL;
  hdma_uart4_rx.Init.Priority = DMA_PRIORITY_HIGH;
  hdma_uart4_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  if (HAL_DMA_Init(&hdma_uart4_rx) != HAL_OK)
  {
    Error_Handler();
  }    
  __HAL_LINKDMA(uartHandle,hdmarx,hdma_uart4_rx);  
  /* UART4_TX Init */
  hdma_uart4_tx.Instance = DMA1_Stream1;
  hdma_uart4_tx.Init.Request = DMA_REQUEST_UART4_TX;
  hdma_uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_uart4_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_uart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  hdma_uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_uart4_tx.Init.Mode = DMA_NORMAL;
  hdma_uart4_tx.Init.Priority = DMA_PRIORITY_HIGH;
  hdma_uart4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  if (HAL_DMA_Init(&hdma_uart4_tx) != HAL_OK)
  {
    Error_Handler();
  }    
  __HAL_LINKDMA(uartHandle,hdmatx,hdma_uart4_tx);  
  /* UART4 interrupt Init */
  HAL_NVIC_SetPriority(UART4_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(UART4_IRQn);
  /* USER CODE BEGIN UART4_MspInit 1 */
  
  /* USER CODE END UART4_MspInit 1 */
  }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==UART4)
  {
  /* USER CODE BEGIN UART4_MspDeInit 0 */

  /* USER CODE END UART4_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_UART4_CLK_DISABLE();

    /**UART4 GPIO Configuration
    PI9     ------> UART4_RX
    PA0_C     ------> UART4_TX
    */
    HAL_GPIO_DeInit(U4_RX_GPIO_Port, U4_RX_Pin);

    HAL_GPIO_DeInit(U4_TX_GPIO_Port, U4_TX_Pin);

    /* UART4 DMA DeInit */
    HAL_DMA_DeInit(uartHandle->hdmarx);
    HAL_DMA_DeInit(uartHandle->hdmatx);

    /* UART4 interrupt Deinit */
    HAL_NVIC_DisableIRQ(UART4_IRQn);
  /* USER CODE BEGIN UART4_MspDeInit 1 */

  /* USER CODE END UART4_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */
/* FILE 在 stdio.h里面定义. */
#pragma import(__use_no_semihosting)
struct __FILE
{  
    int handle;
};

FILE __stdout;
FILE __stdin;

void _sys_exit(int x)
{
    x = x;
}

/* 重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart4, (uint8_t *) (&ch), 1, 5);
    return ch;
}

/* ***************** 向 mboot的数据通讯 提供接口 ***************** */

/* 设置接收缓冲区信息 */
static int uart_for_mboot_set_rxbuf_attr(hw_buf_attr * buf_attr);
/* 设置发送缓冲区信息 */
static int uart_for_mboot_set_txbuf_attr(hw_buf_attr * buf_attr);
/* 开始接收数据 */
static void uart_for_mboot_start_recv(void);
/* 检测是否收到数据 */
static HW_STATUS uart_for_mboot_check_is_rxed(void);
/* 检测是否发送完成 */
static HW_STATUS uart_for_mboot_check_is_txed(void);
/* 获得 rxbuf 的数据 */
static hw_buf_attr *uart_for_mboot_get_data(void);
/* 获得 txbuf 缓冲区地址 */
static hw_buf_attr *uart_for_mboot_get_txbuf(void);
/* 往串口写入数据 */
static void uart_for_mboot_put_data(hw_buf_attr * buf_attr);

/* ***************** 提供给mboot的数据通讯接口实现 ***************** */

volatile HW_STATUS s_is_rxed = HW_FALSE, s_is_rxdata_geted = HW_FALSE;
volatile HW_STATUS s_is_txed = HW_FALSE;
uint32_t MBOOT_CFG_RX_SIZE;
hw_buf_attr s_rxbuf_attr;
hw_buf_attr s_txbuf_attr;

/* 串口 (接收) DMA空闲中断回调函数 */
static void uart_dma_idle_irq_callback(UART_HandleTypeDef *huart, uint16_t size)
{
    if (huart->Instance == UART4)
    {
        s_rxbuf_attr.len  = MBOOT_CFG_RX_SIZE - __HAL_DMA_GET_COUNTER(&hdma_uart4_rx);
        s_is_rxed         = HW_TRUE;    /* 标记有数据被接收到 */
        s_is_rxdata_geted = HW_FALSE;    /* 标记数据没有没取走 */
    }
}

/* 串口 (发送) 完成中断回调函数 */
static void uart_dma_tx_complete_callback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == UART4)
    {
        s_is_txed = HW_TRUE;        /* 标记发送完成 */
    }
}

/* 串口提供给 mboot的操作函数 */
uart_op_for_mboot s_uart_op_for_mboot = 
{
    .set_txbuf_attr = uart_for_mboot_set_txbuf_attr,
    .set_rxbuf_attr = uart_for_mboot_set_rxbuf_attr,
    .start_recv     = uart_for_mboot_start_recv,
    .check_is_rxed  = uart_for_mboot_check_is_rxed,
    .check_is_txed  = uart_for_mboot_check_is_txed,
    .get_txbuf      = uart_for_mboot_get_txbuf,
    .get_data       = uart_for_mboot_get_data,
    .put_data       = uart_for_mboot_put_data
};

/* 设置发送缓冲区信息 */
static int uart_for_mboot_set_txbuf_attr(hw_buf_attr * buf_attr)
{
    /* 参数检验 */
    if (buf_attr == NULL)
    {
        return -1;
    }

    /* 设置 */
    s_txbuf_attr.buf  = buf_attr->buf;  /* 指定发送缓冲区 */
    s_txbuf_attr.len  = buf_attr->len;  /* 指定发送缓冲区大小 */

    return 0;
}

/* 设置接收缓冲区信息 */
static int uart_for_mboot_set_rxbuf_attr(hw_buf_attr * buf_attr)
{
    /* 参数检验 */
    if (buf_attr == NULL)
    {
        return -1;
    }

    /* 设置 */
    s_rxbuf_attr.buf  = buf_attr->buf;  /* 指定接收缓冲区 */
    MBOOT_CFG_RX_SIZE = buf_attr->len;  /* 指定接收缓冲区的大小 */

    return 0;
}

/* 开始接收数据 */
static void uart_for_mboot_start_recv(void)
{
    /* 判断数据是否被取走 */
    if ((s_is_rxed == HW_TRUE) && (s_is_rxdata_geted != HW_TRUE))
    {
        return;
    }
    else
    {
        /* 当接收到的数据被取走后, 才允许 设置 s_is_rxed 为 false */
        s_is_rxed = HW_FALSE;
    }
    
    /* 开始接收数据 */
    HAL_UARTEx_ReceiveToIdle_DMA(&huart4, s_rxbuf_attr.buf, \
                                 MBOOT_CFG_RX_SIZE);
}

/* 检测是否收到数据 */
static HW_STATUS uart_for_mboot_check_is_rxed(void)
{
    return s_is_rxed;
}

/* 检测是否发送完数据 */
static HW_STATUS uart_for_mboot_check_is_txed(void)
{
    return s_is_txed;
}

/* 获得 rxbuf 的数据 */
static hw_buf_attr *uart_for_mboot_get_data(void)
{
    /* 标记接收到的数据被取走 */
    s_is_rxdata_geted = HW_TRUE;

    return &s_rxbuf_attr;
}

/* 获得 txbuf 缓冲区地址 */
static hw_buf_attr *uart_for_mboot_get_txbuf(void)
{
    return &s_txbuf_attr;
}

/* 往串口写入数据 */
static void uart_for_mboot_put_data(hw_buf_attr * buf_attr)
{
    /* 复位发送完成标记 */
    s_is_txed = HW_FALSE;
    
    /* 发送数据 (直接发送不需要 标记同步, 否则需要检测标记) */
#if 0
    HAL_UART_Transmit_DMA(&huart4, buf_attr->buf, buf_attr->len);
#else
    HAL_UART_Transmit(&huart4, buf_attr->buf, buf_attr->len, 500);
#endif

    return;
}

/* ***************** 向 mboot的数据通讯 提供接口和接收配置信息 ***************** */

uart_op_for_mboot *uart_op_for_mboot_register(void)
{
    /* 返回操作函数接口 */
    return &s_uart_op_for_mboot;
}

/* USER CODE END 1 */
